
#include "yinmei.h"
#include <iostream>
#include <sstream>
#include <fstream>
#include <experimental/filesystem>
#include <cstdlib>
#include <ctime>
#include <thread>
#include <chrono>
#include <regex>
#include <algorithm>
#include <cctype>

namespace fs = std::experimental::filesystem;

namespace yinmei {

    std::string PrinterConfig::toOptionsString() const {
        std::ostringstream oss;
        oss << "res=" << resolution 
            << ":unit=" << unit 
            << ":grey=" << grey 
            << ":duplex=" << duplex 
            << ":revert=" << revert;
        return oss.str();
    }

    YinmeiPrinter::YinmeiPrinter() 
        : m_currentStatus(PrintStatus::IDLE)
        , m_lastStatusTime(0)
        , m_completedStatusCount(0) {
        
        try {
            m_workingDir = fs::current_path().string();
        } catch (const std::exception& e) {
            m_workingDir = ".";
        }
    }

    YinmeiPrinter::~YinmeiPrinter() {
    }

    bool YinmeiPrinter::setInputFile(const std::string& filePath) {
        try {
            std::string absolutePath = getAbsolutePath(filePath);
            
            if (!fileExists(absolutePath)) {
                m_lastError = "File not found: " + absolutePath;
                return false;
            }

            if (!isValidImageFile(absolutePath)) {
                m_lastError = "Invalid image file format: " + absolutePath;
                return false;
            }

            m_inputFile = absolutePath;
            m_convertedFile.clear();
            updateStatus(PrintStatus::IDLE);
            
            return true;
        } catch (const std::exception& e) {
            m_lastError = "Error setting input file: " + std::string(e.what());
            return false;
        }
    }

    std::string YinmeiPrinter::getInputFile() const {
        return m_inputFile;
    }

    ConvertResult YinmeiPrinter::convertImage() {
        if (m_inputFile.empty()) {
            m_lastError = "No input file set";
            return ConvertResult::FILE_NOT_FOUND;
        }

        std::string outputFile = generateOutputFileName(m_inputFile);
        return convertImage(outputFile);
    }

    ConvertResult YinmeiPrinter::convertImage(const std::string& outputPath) {
        if (m_inputFile.empty()) {
            m_lastError = "No input file set";
            return ConvertResult::FILE_NOT_FOUND;
        }

        if (!fileExists(m_inputFile)) {
            m_lastError = "Input file not found: " + m_inputFile;
            return ConvertResult::FILE_NOT_FOUND;
        }

        updateStatus(PrintStatus::CONVERTING);

        try {
            std::ostringstream cmd;
            cmd << "./img2prn -t2 --options=\"" << m_config.toOptionsString() 
                << "\" --angle=" << m_config.angle 
                << " -o \"" << outputPath << "\" \"" << m_inputFile << "\"";

            std::string output;
            bool success = executeCommand(cmd.str(), output);

            if (success && fileExists(outputPath)) {
                m_convertedFile = outputPath;
                updateStatus(PrintStatus::IDLE);
                return ConvertResult::SUCCESS;
            } else {
                m_lastError = "Conversion failed: " + output;
                updateStatus(PrintStatus::ERROR);
                return ConvertResult::CONVERT_FAILED;
            }
        } catch (const std::exception& e) {
            m_lastError = "Exception during conversion: " + std::string(e.what());
            updateStatus(PrintStatus::ERROR);
            return ConvertResult::CONVERT_FAILED;
        }
    }

    std::string YinmeiPrinter::getConvertedFilePath() const {
        return m_convertedFile;
    }

    bool YinmeiPrinter::startPrint() {
        if (m_convertedFile.empty()) {
            m_lastError = "No converted file available. Call convertImage() first.";
            return false;
        }
        return startPrint(m_convertedFile);
    }

    bool YinmeiPrinter::startPrint(const std::string& binFilePath) {
        if (!fileExists(binFilePath)) {
            m_lastError = "Binary file not found: " + binFilePath;
            return false;
        }

        try {
            updateStatus(PrintStatus::PRINTING);
            resetStatusCounter();

            // 构建打印命令
            std::ostringstream cmd;
            cmd << "./lpusb --out-file=\"" << binFilePath << "\"";

            std::string output;
            bool success = executeCommand(cmd.str(), output);

            if (!success) {
                m_lastError = "Print command failed: " + output;
                updateStatus(PrintStatus::ERROR);
                return false;
            }

            return true;
        } catch (const std::exception& e) {
            m_lastError = "Exception during printing: " + std::string(e.what());
            updateStatus(PrintStatus::ERROR);
            return false;
        }
    }

    PrintStatus YinmeiPrinter::queryPrintStatus() {
        try {
            std::string cmd = "./lpusb --ioc-vendor=0xF4 --in-len=4";
            std::string output;
            
            if (!executeCommand(cmd, output)) {
                m_lastError = "Failed to query print status";
                updateStatus(PrintStatus::ERROR);
                return m_currentStatus;
            }

            PrintStatus status = parseStatusOutput(output);
            updateStatus(status);
            
            return status;
        } catch (const std::exception& e) {
            m_lastError = "Exception during status query: " + std::string(e.what());
            updateStatus(PrintStatus::ERROR);
            return PrintStatus::ERROR;
        }
    }

    bool YinmeiPrinter::isPrintCompleted() {
        PrintStatus status = queryPrintStatus();
        
        if (status == PrintStatus::PRINT_ENDING) {
            time_t currentTime = time(nullptr);
            
            if (m_lastStatusTime == 0) {
                m_lastStatusTime = currentTime;
                m_completedStatusCount = 1;
                return false;
            }
            
            if (currentTime - m_lastStatusTime >= 1) {
                m_completedStatusCount++;
                m_lastStatusTime = currentTime;
                
                if (m_completedStatusCount >= 10) {
                    updateStatus(PrintStatus::COMPLETED);
                    return true;
                }
            }
        } else {
            resetStatusCounter();
        }
        
        return false;
    }

    void YinmeiPrinter::setPrinterConfig(const PrinterConfig& config) {
        m_config = config;
    }

    PrinterConfig YinmeiPrinter::getPrinterConfig() const {
        return m_config;
    }

    void YinmeiPrinter::setWorkingDirectory(const std::string& workDir) {
        try {
            if (fs::exists(workDir) && fs::is_directory(workDir)) {
                m_workingDir = fs::absolute(workDir).string();
            }
        } catch (const std::exception& e) {
            m_lastError = "Failed to set working directory: " + std::string(e.what());
        }
    }

    std::string YinmeiPrinter::getWorkingDirectory() const {
        return m_workingDir;
    }

    std::string YinmeiPrinter::getLastError() const {
        return m_lastError;
    }

    std::string YinmeiPrinter::getAbsolutePath(const std::string& path) {
        try {
            if (fs::path(path).is_absolute()) {
                return path;
            }
            return fs::absolute(path).string();
        } catch (const std::exception& e) {
            return path;
        }
    }

    bool YinmeiPrinter::fileExists(const std::string& path) {
        try {
            return fs::exists(path) && fs::is_regular_file(path);
        } catch (const std::exception& e) {
            std::ifstream file(path);
            return file.good();
        }
    }

    bool YinmeiPrinter::executeCommand(const std::string& command, std::string& output) {
        FILE* pipe = popen(command.c_str(), "r");
        if (!pipe) {
            output = "Failed to execute command";
            return false;
        }

        char buffer[256];
        std::ostringstream result;
        
        while (fgets(buffer, sizeof(buffer), pipe) != nullptr) {
            result << buffer;
        }

        int exitCode = pclose(pipe);
        output = result.str();
        
        return exitCode == 0;
    }

    std::string YinmeiPrinter::generateOutputFileName(const std::string& inputFile) {
        try {
            fs::path inputPath(inputFile);
            std::string baseName = inputPath.stem().string();
            
            fs::path outputPath = fs::path(m_workingDir) / (baseName + "_output.bin");
            return outputPath.string();
        } catch (const std::exception& e) {
            
            size_t lastSlash = inputFile.find_last_of("/\\");
            size_t lastDot = inputFile.find_last_of('.');
            
            std::string baseName;
            if (lastSlash != std::string::npos) {
                if (lastDot != std::string::npos && lastDot > lastSlash) {
                    baseName = inputFile.substr(lastSlash + 1, lastDot - lastSlash - 1);
                } else {
                    baseName = inputFile.substr(lastSlash + 1);
                }
            } else {
                if (lastDot != std::string::npos) {
                    baseName = inputFile.substr(0, lastDot);
                } else {
                    baseName = inputFile;
                }
            }
            
            return m_workingDir + "/" + baseName + "_output.bin";
        }
    }

    PrintStatus YinmeiPrinter::parseStatusOutput(const std::string& output) {
        std::regex statusRegex(R"(([0-9A-Fa-f]{2})\s+([0-9A-Fa-f]{2})\s+([0-9A-Fa-f]{2})\s+([0-9A-Fa-f]{2}))");
        std::smatch match;
        
        if (std::regex_search(output, match, statusRegex)) {
            std::string firstByte = match[1].str();
            std::string secondByte = match[2].str();
            
            std::transform(firstByte.begin(), firstByte.end(), firstByte.begin(), ::toupper);
            std::transform(secondByte.begin(), secondByte.end(), secondByte.begin(), ::toupper);
            
            if (firstByte == "09" && secondByte == "44") {
                return PrintStatus::PRINTING;
            } else if (firstByte == "08" && secondByte == "00") {
                return PrintStatus::PRINT_ENDING;
            }
        }
        
        return m_currentStatus;
    }

    bool YinmeiPrinter::isValidImageFile(const std::string& filePath) {
        try {
            fs::path path(filePath);
            std::string extension = path.extension().string();
            
            std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
            
            return extension == ".jpg" || extension == ".jpeg" || 
                   extension == ".png" || extension == ".bmp" || 
                   extension == ".gif" || extension == ".tiff" || 
                   extension == ".tif";
        } catch (const std::exception& e) {
            
            size_t lastDot = filePath.find_last_of('.');
            if (lastDot == std::string::npos) {
                return false;
            }
            
            std::string extension = filePath.substr(lastDot);
            std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower);
            
            return extension == ".jpg" || extension == ".jpeg" || 
                   extension == ".png" || extension == ".bmp" || 
                   extension == ".gif" || extension == ".tiff" || 
                   extension == ".tif";
        }
    }

    void YinmeiPrinter::updateStatus(PrintStatus status) {
        m_currentStatus = status;
    }

    void YinmeiPrinter::resetStatusCounter() {
        m_lastStatusTime = 0;
        m_completedStatusCount = 0;
    }

} // namespace yinmei
